//
//  main.m
//  OC07_内存管理
//
//  Created by Scott on 16/3/3.
//  Copyright © 2016年 Scott. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "Person.h"
#import <objc/runtime.h>
#import "Student.h"
#import "Phone.h"
/** 知识点: 对象及对象的实例变量在堆中的地址. */
void chapterOfSizeofInstance() {
    
    /** 
     * 定义Person类: 实例变量可见度为 @public
     * 详见 Person类 文件
     */
    
#if 0
    /** 创建对象Person1, 并赋值 */
    Person *person1 = [[Person alloc] init];
    person1->_name = @"zhangsan";
    person1->_sex = @"male";
    person1->_age = 18;
    
    /* 直接显示 对象大小 . 需要导入<objc/runtime.h> 文件 */
    NSLog(@"size:%ld", class_getInstanceSize([person1 class]));
    
    NSLog(@"************************************************");
    //行数
    NSLog(@"---%d---(person1)对象和对象的实例变量在堆中的地址---", __LINE__);
    NSLog(@"person1      add:%p", person1);
    NSLog(@"person1 name add:%p", &person1->_name);
    NSLog(@"person1 age  add:%p", &person1->_age);
    NSLog(@"person1 sex  add:%p", &person1->_sex);
    
    NSLog(@"***********************************************");
    NSLog(@"---如果实例变量类型是对象类型, 指针内容是一个指针---");
    NSLog(@"---%d---(person1)实例变量的指针指向的地址---", __LINE__);
    NSLog(@"person1 name contant :%p", person1->_name);
    NSLog(@"person1 sex  contant :%p", person1->_sex);
    NSLog(@"字符串(zhangsan)   add:%p", @"zhangsan");
    NSLog(@"字符串(male)       add:%p", @"male");
    /*!^ 结论
     * 实例变量的指针最终指向了字符串常量首地址
     */
#endif
#if 0
    NSLog(@"---如果实例变量类型是非对象类型, 指针内容即为值---");
    NSLog(@"person1 age  contant :%p", person1->_age);
    NSLog(@"person1 age  contant :%ld", person1->_age);
    
    
    /** 创建另一个对象Person2 */
    Person *person2 = [[Person alloc] init];
    person2 = person1;
    
    NSLog(@"************************************************");
    NSLog(@"---%d---(person2)对象和对象的实例变量在堆中的地址---", __LINE__);
    NSLog(@"person2      add:%p", person2);
    NSLog(@"person2 name add:%p", &person2->_name);
    NSLog(@"person2 age  add:%p", &person2->_age);
    NSLog(@"person2 sex  add:%p", &person2->_sex);
    /*!^ 结论
     * person2 指向了person1的首地址
     * person2 的实例变量地址也指向person1 的实例变量首地址
     */
    
    NSLog(@"************************************************");
    NSLog(@"---%d---(person2)实例变量的指针指向的地址---", __LINE__);
    NSLog(@"person2 name contant :%p", person2->_name);
    NSLog(@"person2 sex  contant :%p", person2->_sex);
    NSLog(@"字符串(zhangsan)   add:%p", @"zhangsan");
    NSLog(@"字符串(male)       add:%p", @"male");
    
    
    /* 使用copy方法创建person3 对象 */
//    Person *person3 = [person1 copy];
    
//    NSLog(@"************************************************");
//    NSLog(@"---%d---(person3)对象和对象的实例变量在堆中的地址---", __LINE__);
//    NSLog(@"person3      add:%p", person3);
//    NSLog(@"person3 name add:%p", &person3->_name);
//    NSLog(@"person3 age  add:%p", &person3->_age);
//    NSLog(@"person3 sex  add:%p", &person3->_sex);
//    
//    
//    NSLog(@"************************************************");
//    NSLog(@"---%d---(person3)实例变量的指针指向的地址---", __LINE__);
//    NSLog(@"person3 name contant :%p", person3->_name);
//    NSLog(@"person3 sex  contant :%p", person3->_sex);
//    NSLog(@"字符串(zhangsan)   add:%p", @"zhangsan");
//    NSLog(@"字符串(male)       add:%p", @"male");
//    
#endif
    
}

/** 知识点: 内存管理. */
void chapterOfManageMemory() {
    
}

/** 知识点: 影响引用计数的方法. */
void chapterOfReferenceCounting() {
    
    /** alloc 方法. */
    Person *per = [[Person alloc] init];
    //在ARC模式下不可以调用retain,release和autorelease方法.
    NSLog(@"per count :%ld",[per retainCount]);
    /** retain 方法. */
    [per retain];
    
    NSLog(@"per retain count:%ld",[per retainCount]);
    
    
    Person *per2 = [per retain];
    NSLog(@"per count:%ld",[per retainCount]);
    NSLog(@"per2 count:%ld",[per2 retainCount]);
    
    Person *per3 = [[Person alloc] init];
    NSLog(@"per3 count:%ld",[per3 retainCount]);
    [per3 release];
    per3 = [per retain];
    /** release 方法. */
    [per release];
    [per2 release];
    NSLog(@"per1 count:%ld",[per retainCount]);
    NSLog(@"per2 count:%ld",[per2 retainCount]);
    
    
    /** autorelease 方法. */
    Person *per4 = [[Person alloc] init];
    @autoreleasepool {
        /** 自动释放池. */
        [per4 retain];
        NSLog(@"per4 count: %ld",[per4 retainCount]);
        [per4 autorelease];
        NSLog(@"per4 count: %ld",[per4 retainCount]);
        
        
        
    }
    [per4 release];
    NSLog(@"per4 count: %ld",[per4 retainCount]);
    /** autoreleasespool 自动释放池. */
    
    /** dealloc 方法.(当引用计数为0时,系统自动调用dealloc方法释放内存空间. 注: 绝对不可以程序员自己调用) */
    
    
    //内存管理机制:引用计数机制
    //内存管理方式:MRC 手动,ARC 自动
    //内存管理原则:在一块代码块中必须保持引用计数为0;
    
}

/** 知识点: copy方法. */
void chapterOfCopy() {
    Person *per = [[Person alloc] initWithName:@"thor" withSex:@"M" withAge:23 withWorkStation:@"大连"];
    
    Person *per2 = [per copy];
    
    NSLog(@"per address: %p",per);
    NSLog(@"per2 address: %p",per2);
    
    NSLog(@"per name content: %p", per.name);
    NSLog(@"per2 name content: %p",per2.name);
    
    NSLog(@"per sex content: %p",per.sex);
    NSLog(@"per2 sex content: %p",per2.sex);
    
    [per release];
    
    
}

/** 知识点: 属性的内部实现原理. */
void chapterOfMemoryForSetterAndGetter() {
    //详见Person.m文件
    
}

/** 知识点: dealloc内释放实例变量. */
void chapterOfDealloc() {
    
    //详见Person.M文件
    
}

/** 知识点: 便利构造器方法的实现原理. */
void chapterOfMemoryForConstructor() {
    
    //详见Person.m文件
    
    //用遍历构造器方法创建Person类的对象
    Person *per1 = [Person personWithName:@"laohei" withSex:@"M" withAge:19 withWorkStation:@"沈阳师范"];
    //不需要自己release
//  [per1 release];
    
    //用自定义初始化方法创建Person类的对象
    Person *per2 = [[Person alloc] initWithName:@"laozhou" withSex:@"M" withAge:22 withWorkStation:@"大连东软"];
    [per2 release]; //和alloc相对应.
    
}

/** 知识点: collection 的内存管理. */
void chapterOfMemoryForCollection() {
    Person *per3 = [[Person alloc] initWithName:@"sichangbao" withSex:@"M" withAge:23 withWorkStation:@"未知"];
    Person *per4 = [[Person alloc] initWithName:@"laochen" withSex:@"M" withAge:22 withWorkStation:@"沈阳师范"];
    
    NSLog(@"per3 count: %ld", [per3 retainCount]);
    NSLog(@"per4 count: %ld", [per4 retainCount]);
    
    NSMutableArray *array = [NSMutableArray arrayWithObjects:per3,per4, nil];
    
    NSLog(@"添加进数组之后的引用计数:");
    NSLog(@"per3 count: %ld", [per3 retainCount]);
    NSLog(@"per4 count: %ld", [per4 retainCount]);
    
    [per3 release];
    [per4 release];
    
    NSLog(@"per3 count: %ld", [per3 retainCount]);
    NSLog(@"per4 count: %ld", [per4 retainCount]);
    
    [array removeAllObjects];
    
    //集合类型的内存管理:
    //1.当把对象添加到集合类中的时候,对象的引用计数会自动加一.
    //2.当把对象从集合类中取出时,对象的引用计数会自动减一.
    //3.集合内的对象的引用计数由集合自己来控制,不需要程序员手动加一.
}

//知识点KVC
void chapterOfKVC(){
    //KVC :Key Value Coding 键值编码
    //间接的访问对象里的实例变量
    //根据key值在类里找有没有相对应的setter或getter方法.
    //如果没有setter或getter方法,继续寻找有没有相同名字的实例变量(例; _name或者name).
    //如果都没有就会调用setValue:forUndefinedKey:或者valueforUndefineKey: 方法.通常会造成程序的崩溃.

    
    Student *stu1 = [[Student alloc] init];
    
    //KVC方法 给属性赋值.
    [stu1 setValue:@"DLS160508" forKey:@"className"];
    //KVC方法 给属性取值
    NSString *str = [stu1 valueForKey:@"className"];
    NSLog(@"stu1 className: %@",str);
    
    //给stu的myPhone 属性赋值
    Phone *pho = [[Phone alloc] init];
//    [pho setValue:@"red" forKey:@"color"];
    
    [stu1 setValue:pho forKey:@"myPhone"];
    
    
    
    NSString *phoColorStr = [[stu1 valueForKey:@"myPhone"] valueForKey:@"color"];
    NSLog(@"stu1 myPhone: %@",phoColorStr);
    //重写setValue:forUndefinedKey:和value:forUndefinedKey详见Student.m文件.
    
    //KVC 给属性的属性赋值
    [stu1 setValue:@"black" forKeyPath:@"myPhone.color"];
    //KVC 给属性的属性取值
    NSString *phoColorStr2 = [stu1 valueForKeyPath:@"myPhone.color"];
    
    NSLog(@"stu1 myPhone color : %@",phoColorStr2);
    
    NSDictionary *dic = @{
                          @"className":@"DLS160507",
                          @"myPhone": pho
                          
                          };
    Student *stu2 = [[Student alloc] init];
    [stu2 setValuesForKeysWithDictionary:dic];
    
    NSLog(@"stu2: %@",stu2);
}



#pragma mark - MAIN FUNCTION
int main(int argc, const char * argv[]) {
        
#pragma mark - 知识点1: 对象及对象的实例变量在堆中的地址
//    chapterOfSizeofInstance();

#pragma mark - 知识点2: 内存管理
//    chapterOfManageMemory();
    
#pragma mark - 知识点3: 影响引用计数的方法
//    chapterOfReferenceCounting();

#pragma mark - 知识点4: copy方法
//    chapterOfCopy();

#pragma mark - 知识点5: 属性的内部实现原理
//    chapterOfMemoryForSetterAndGetter();

#pragma mark - 知识点6: dealloc内释放实例变量
 //   chapterOfDealloc();

#pragma mark - 知识点7: 便利构造器方法的实现原理
//    chapterOfMemoryForConstructor();

#pragma mark - 知识点8: collection 的内存管理
//    chapterOfMemoryForCollection();

    
    chapterOfKVC();

    return 0;
}






